var StringBuilder = function() {
    this._string = [];
    this._delimiter = "";
    var self = this;

    this.append = function(str) {
        self._string.push(str);
        return self;
    };

    this.setDelimiter = function(del) {
        self._delimiter = del;
    };

    this.clear = function() {
        self._string = [];
    };

    this.toString = function() {
        return self._string.join(self._delimiter);
    };

    return this;
};
var Helper = function(){
	var _self = this;
	this.isLabel = function(x){
		if(x === "label") return true;
		else return false;
	};
	this.isBonus = function(x){
		if(x === "bonus") return true;
		else return false;
	};
	this.isSumField = function(x){
		if(x.indexOf("sum") === 0) return true;
		else return false;
	};
	this.generateHook = function(r, c){
		return r+"__"+c;
	};
	this.getHook = function(r, c){
		return r+"__"+c;
	};
	this.getRC = function(hook){
		var ret = {};
		ret.row = hook.split("__")[0];
		ret.column = hook.split("__")[1];
		return ret;
	};
};

var FieldGenerator = function(){
	var _self = this;
	this.generateField = function(row, column){
		return {
			value : undefined,
			row: row,
			column: column,
			flag : false,
			called: false
		};
	};
	this.generateMatrix = function(rows, columns){
		var matrix = {};
		$.each(columns, function(index_b, value_b){
			var inner = {};
			$.each(rows, function(index_a, value_a){
				inner[value_a.name] = _self.generateField(value_b.name, value_a.name);
			});
			matrix[value_b.name] = inner;
		});
		return matrix;
	};
};

var Dice = function(){
	var _self = this;
	var number;
	var selectedFlag;
	_self.setNumber = function(_number){
		_self.number = _number;
	};
	_self.setSelected = function(_selected){
		_self.selectedFlag = _selected;
	};
	(function(){
		_self.setNumber(-1);
		_self.setSelected(false);
	})();
};

var CONSTANTS = {
	MAX_INDEX : 7,
	MIN_INDEX : 8
};
var ONE = {
	name : "one",
	sign : "1"
};
var TWO = {
	name : "two",
	sign : "2"
};
var THREE = {
	name : "three",
	sign : "3"
};
var FOUR = {
	name : "four",
	sign : "4"
};
var FIVE = {
	name : "five",
	sign : "5"
};
var SIX = {
	name : "six",
	sign : "6"
};
var MAX = {
	name : "max",
	sign : "+"
};
var MIN = {
	name : "min",
	sign : "-"
};
var KENTA = {
	name : "kenta",
	sign : "K"
};
var TRILLING = {
	name : "trilling",
	sign : "T"
};
var FULL = {
	name : "full",
	sign : "F"
};
var POKER = {
	name : "poker",
	sign : "P"
};
var YAMB = {
	name : "yamb",
	sign : "Y"
};

var SUM_N = {
	name : "sum_n",
	sign : "&#8721;"
};
var SUM_MM = {
	name : "sum_mm",
	sign : "&#8721;"
};
var SUM_LG = {
	name : "sum_lg",
	sign : "&#8721;"
};

var DOWN = {
	name : "down",
	sign : "&#8659;"
};
var UP = {
	name : "up",
	sign : "&#8657;"
};
var SPLIT = {
	name : "split",
	sign : "&#8659;&#8657;"
};
var FREE = {
	name : "free",
	sign : "FREE"
};
var CALL = {
	name : "call",
	sign : "CALL"
};
var FIRST_HAND = {
	name : "firsthand",
	sign : "FH"
};
var LABEL = {
	name : "label",
	sign : ""
}
var BONUS = {
	name : "bonus",
	sign : ""
}

var rows_n = [ONE, TWO, THREE, FOUR, FIVE, SIX];
var row_sn = [SUM_N];
var rows_mm = [MAX, MIN];
var row_smm = [SUM_MM];
var rows_lg = [KENTA, TRILLING, FULL, POKER, YAMB];
var row_slg = [SUM_LG];

var rows = rows_n.concat(row_sn).concat(rows_mm).concat(row_smm).concat(rows_lg).concat(row_slg);

var columns = [LABEL, DOWN, UP, FREE, CALL, FIRST_HAND, SPLIT, BONUS];
var MAX_MOVES_COUNT = 2;
var DICE_COUNT = 5;
var BONUSES = {
	kenta : 40,
	trilling : 30,
	full : 50,
	poker : 70,
	yamb : 90,
	numbers: 30,
	numbersLimit: 60,
	maxmin: 30,
	maxminLimit: 60
};

var OptionsOverride = function(options){
	(function(){
		if(options.SMALL){
			rows_lg = _.without(rows_lg, TRILLING);
			rows = rows_n.concat(row_sn).concat(rows_mm).concat(row_smm).concat(rows_lg).concat(row_slg);
			columns = _.without(columns, CALL, SPLIT);
		};
		if(options.DICE_COUNT){
			DICE_COUNT = options.DICE_COUNT;
		}
	})();
};

var Board = function(){
	var _self = this;
	var helper = new Helper();

	this.draw = function(rows, columns){
		return _self.createBoard(rows, columns) + _self.createRollControl() + _self.createDiceBox();
	};

	this.createBoard = function(rows, columns){
		var html = new StringBuilder();
		html.append("<table class='board'>");
		html.append("<tr>");
		$.each(columns, function(index, value){
			var cls = "";
			if(value.sign === "") cls = "no-bground";
			html.append("<th class='").append(cls).append("'>").append(value.sign).append("</th>");
		});
		html.append("</tr>");
		$.each(rows, function(index_b, value_b){
			html.append("<tr>");
			$.each(columns, function(index_a, value_a){
				var _val = "";
				var _class = "";
				if(helper.isLabel(value_a.name)){
					_val = value_b.sign;
					_class = "label";
				}
				else if(helper.isBonus(value_a.name)){
					if(BONUSES[value_b.name]){
						_val = "+" + BONUSES[value_b.name];	
						_class += "bonus_info "
					}
					_class += "bonus";
				}
				else if(helper.isSumField(value_b.name)){
					_class = "sum label";
				}
				var _hook = helper.generateHook(value_b.name, value_a.name);
				html.append("<td class='")
				    .append(_class)
				    .append(" ")
				    .append(_hook)
				    .append("' ")
				    .append("data-hook='")
				    .append(_hook)
				    .append("'>")
				    .append(_val)
				    .append("</td>");
			});
			html.append("</tr>");
		});
		html.append("</table>");
		return html.toString();
	};
	this.createRollControl = function(){
		return "<a class='roll' data-cnt=''>Roll dice<span id='counter'></span></a>";
	};
	this.createDiceBox = function(){
		return "<div id='dice-box' class='dice-box'></div>";	
	}
	this.drawDice = function(arr){
		$("#dice-box").html("")
		for(var i=0; i<arr.length; i++){
			if(arr[i]){
				var selected = "";
				if(arr[i].selectedFlag){
					selected = " selected";
				}
				$("#dice-box").append("<div class='dice"+selected+"' data-x='"+i+"'>"+arr[i].number+"</div>");
			}else{
				break;
			}
		}	
	};
	this.updateMovesCounter = function(movesLeft, rollFlag){
		if(!rollFlag) {
			$(".roll").addClass("disabled");
		}
		else {
			$(".roll").removeClass("disabled");
		}
		$("#counter").html(" (" + (movesLeft+1) + ")");
		
	};
	this.setFieldVisibility = function(flag, r, c){
		if(helper.isLabel(c) || helper.isBonus(c)){

		}
		else if(flag){
			$(".board").find("."+ helper.getHook(r, c)).addClass("enabled").removeClass("disabled");
		}
		else if(!flag){
			$(".board").find("."+ helper.getHook(r, c)).addClass("disabled").removeClass("enabled");
		}
		
	};
	this.setValue = function(val, r, c){
		if(val){
			$(".board").find("."+ helper.getHook(r, c)).html(val);
		}
	};
	this.showMessage = function(msg){
		alert(msg);
	};
};





var Yamb = function(options){
	var _self = this;

	(function(options){
		if(options){
			new OptionsOverride(options);	
		}
	})(options);

	var helper = new Helper();
	var fg = new FieldGenerator();
	var brd = new Board();

	_self.rollFlag = true;
	_self.calledFlag = false;

	_self.diceCollection = new Array(DICE_COUNT);
	_self.movesLeft = MAX_MOVES_COUNT;
	
	_self.matrix = fg.generateMatrix(rows, columns);
	
	this.drawBoard = function(){
		return brd.draw(rows, columns);
	};
	this.refreshMatrix = function(){
		$.each(columns, function(index_b, value_b){
			if(!helper.isLabel(value_b.name) && !helper.isBonus(value_b.name)){
				switch(value_b.name){
					case DOWN.name: 
						_self.checkDOWN();
						break;
				    case UP.name: 
				    	_self.checkUP();
						break;
				    case FREE.name: 
				    	_self.checkFREE();
						break;
				    case CALL.name: 
				    	_self.checkCALL();
						break;
				    case FIRST_HAND.name: 
				    	_self.checkFH();
						break;
				    case SPLIT.name: 
				    	_self.checkSPLIT();
						break;
				}
			}
		});	
		return _self;
	};
	this.lockMatrix = function(){
		$.each(columns, function(index_b, value_b){
			$.each(rows, function(index_a, value_a){
				if(!helper.isLabel(value_b.name) && !helper.isBonus(value_b.name)){
					_self.matrix[value_b.name][value_a.name].flag = false;
				}
			});
		});
	};
	this.refreshBoard = function(){
		$.each(columns, function(index_b, value_b){
			$.each(rows, function(index_a, value_a){
				if(!helper.isLabel(value_b.name)){
					brd.setFieldVisibility(_self.matrix[value_b.name][value_a.name].flag, value_a.name, value_b.name);
					brd.setValue(_self.matrix[value_b.name][value_a.name].value, value_a.name, value_b.name);
				}
			});
		});
		brd.updateMovesCounter(_self.movesLeft, _self.rollFlag);
		brd.drawDice(_self.diceCollection);
		return _self;
	};
	this.checkDOWN = function(){
		var part = _self.matrix[DOWN.name];
		for(var i=0; i<_.size(part); i++){
			var item = part[rows[i].name];
			if(!helper.isSumField(item.column) && !helper.isLabel(item.row) && !helper.isBonus(item.row)){
				if (!item.value){
					item.flag = true;
					break;
				}else{
					item.flag = false;
				}
			}
		}
	};
	this.checkUP = function(){
		var part = _self.matrix[UP.name];
		for(var i=_.size(part)-1; i>=0; i--){
			var item = part[rows[i].name];
			if(!helper.isSumField(item.column) && !helper.isLabel(item.row) && !helper.isBonus(item.row)){
				if (!item.value){
					item.flag = true;
					break;
				}else{
					item.flag = false;
				}
			}
		}
	};
	this.checkFREE = function(){
		var part = _self.matrix[FREE.name];
		for(var i=0; i<_.size(part); i++){
			var item = part[rows[i].name];
			if(!helper.isSumField(item.column) && !helper.isLabel(item.row) && !helper.isBonus(item.row)){
				if (!item.value){
					item.flag = true;
				}
			}
		}
	};
	this.checkCALL = function(){
		var part = _self.matrix[CALL.name];
		for(var i=0; i<_.size(part); i++){
			var item = part[rows[i].name];
			if(!helper.isSumField(item.column) && !helper.isLabel(item.row) && !helper.isBonus(item.row)){
				if ((!item.value && _self.movesLeft === MAX_MOVES_COUNT - 1) || item.called){
					item.flag = true;
				}else{
					item.flag = false;
				}	
			}
		}
	};
	this.checkFH = function(){
		var part = _self.matrix[FIRST_HAND.name];
		for(var i=0; i<_.size(part); i++){
			var item = part[rows[i].name];
			if(!helper.isSumField(item.column) && !helper.isLabel(item.row) && !helper.isBonus(item.row)){
				if (!item.value && _self.movesLeft === MAX_MOVES_COUNT - 1){
					item.flag = true;
				}else{
					item.flag = false;
				}
			}
		}
	};
	this.checkSPLIT = function(){
		var part = _self.matrix[SPLIT.name];
		for(var i=CONSTANTS.MAX_INDEX; i>=0; i--){
			var item = part[rows[i].name];
			if(!helper.isSumField(item.column) && !helper.isLabel(item.row) && !helper.isBonus(item.row)){
				if (!item.value){
					item.flag = true;
					break;
				}	
			}
		}
		for(var i=CONSTANTS.MIN_INDEX; i<_.size(part); i++){
			var item = part[rows[i].name];
			if(!helper.isSumField(item.column) && !helper.isLabel(item.row) && !helper.isBonus(item.row)){
				if (!item.value){
					item.flag = true;
					break;
				}	
			}
		}
	};
	
	this.getDiceAsIntegerArray = function(){
		var ret = new Array();
		for(var i=0; i<_self.diceCollection.length; i++){
			if(_self.diceCollection[i].selectedFlag){
				ret.push(_self.diceCollection[i].number);
			}
		}
		return ret;
	};
	this.enterValue = function(hook){
		var rc = helper.getRC(hook);
		if(_self.matrix[rc.column][rc.row].flag){
			var proceed = true;
			if(rc.column === CALL.name){
				if(!_self.matrix[rc.column][rc.row].called){
					proceed = false;
					_self.calledFlag = true;
					_self.matrix[rc.column][rc.row].called = true;
					_self.refresh({lock : true, exceptionField : rc});
				}
			}
			if(proceed){
				var hr = new HorizontalRules();
				var ret = hr.calculateValue(rc.row, _self.getDiceAsIntegerArray());
				if(ret.status){
					_self.matrix[rc.column][rc.row].value = ret.value.toString();
					_self.matrix[rc.column][rc.row].flag = false;
					_self.updateSum(rc.column, rc.row);
					_self.resetMovesLeft();
					_self.rollFlag = true;
					_self.diceCollection = new Array(DICE_COUNT);
					_self.matrix[rc.column][rc.row].called = false;
					_self.refresh({lock : true});
					_self.calledFlag = false;
				}
				else{
					brd.showMessage("Not enough selected dice");
				}	
			}
		}
	};
	this.updateSum = function(column, row){
		for(var i=0; i<rows_n.length; i++){
			if(row === rows_n[i].name){
				var x = _self.getSum_n(column);
				_self.matrix[column][SUM_N.name].value = x;
				_self.updateSumOfSum({rows : 'n'});
				if(row === ONE.name){
					var y = _self.getSum_mm(column);
					_self.matrix[column][SUM_MM.name].value = y;
					_self.updateSumOfSum({rows : 'mm'});
				}
				return;
			}
		}
		for(var i=0; i<rows_mm.length; i++){
			if(row === rows_mm[i].name){
				var x = _self.getSum_mm(column);
				_self.matrix[column][SUM_MM.name].value = x;
				_self.updateSumOfSum({rows : 'mm'});
				return;
			}
		}
		for(var i=0; i<rows_lg.length; i++){
			if(row === rows_lg[i].name){
				var x = _self.getSum_lg(column);
				_self.matrix[column][SUM_LG.name].value = x;
				_self.updateSumOfSum({rows : 'lg'});
				return;
			}
		}
	}
	this.updateSumOfSum = function(data){
		var sum = 0;
		switch(data.rows){
			case 'n' : {
				for(var i=0; i<columns.length; i++){
					if(!helper.isLabel(columns[i].name) && !helper.isBonus(columns[i].name)){
						if(_self.matrix[columns[i].name][SUM_N.name].value !== undefined){
							sum += parseInt(_self.matrix[columns[i].name][SUM_N.name].value);
						}
					}
				}
				_self.matrix[BONUS.name][SUM_N.name].value = sum;
				break;
			};
			case 'mm' : {
				for(var i=0; i<columns.length; i++){
					if(!helper.isLabel(columns[i].name) && !helper.isBonus(columns[i].name)){
						if(_self.matrix[columns[i].name][SUM_MM.name].value !== undefined){
							sum += parseInt(_self.matrix[columns[i].name][SUM_MM.name].value);
						}
					}
				}
				_self.matrix[BONUS.name][SUM_MM.name].value = sum;
				break;
			};
			case 'lg' : {
				for(var i=0; i<columns.length; i++){
					if(!helper.isLabel(columns[i].name) && !helper.isBonus(columns[i].name)){
						if(_self.matrix[columns[i].name][SUM_LG.name].value !== undefined){
							sum += parseInt(_self.matrix[columns[i].name][SUM_LG.name].value);
						}
					}
				}
				_self.matrix[BONUS.name][SUM_LG.name].value = sum;
				break;
			};
		}
	};
	this.getSum_n = function(column){
		var branch = _self.matrix[column];
		var ret = 0;
		for(var i=0; i<rows_n.length; i++){
			if(branch[rows_n[i].name].value){
				ret += parseInt(branch[rows_n[i].name].value);
			}
		}
		if(ret) {
			if(ret >= BONUSES.numbersLimit) ret += parseInt(BONUSES.numbers);
			return ret;
		}
		else return undefined;
	};
	this.getSum_mm = function(column){
		var branch = _self.matrix[column];
		var ret = 0;
		if(branch["one"].value && branch["max"].value && branch["min"].value){
			ret = (branch["max"].value - branch["min"].value) * branch["one"].value;
		}
		if(ret) {
			if(ret >= BONUSES.maxminLimit) ret += parseInt(BONUSES.maxmin);
			return ret;
		}
		else return undefined;
	};
	this.getSum_lg = function(column){
		var branch = _self.matrix[column];
		var ret = 0;
		for(var i=0; i<rows_lg.length; i++){
			if(branch[rows_lg[i].name].value){
				ret += parseInt(branch[rows_lg[i].name].value);
			}
		}
		if(ret) return ret;
		else return undefined;
	};
	this.refresh = function(options){
		if(options.lock){
			_self.lockMatrix();
			if(options.exceptionField){
				_self.matrix[options.exceptionField.column][options.exceptionField.row].flag = true;
			}
		}else{
			if(!_self.calledFlag){
				_self.refreshMatrix();	
			}
		}
		_self.refreshBoard();
		return _self;		
	};
	this.rollDice = function(){
		if(_self.rollFlag){
			for(var i = 0; i<_self.diceCollection.length; i++){
				var dice = new Dice();
				dice.setNumber(parseInt(Math.random()*6+1));
				if(!_self.diceCollection[i]) {
					_self.diceCollection[i] = dice;
				}
				else{
					if(!_self.diceCollection[i].selectedFlag){
						_self.diceCollection[i] = dice;
					}
				}
			}
			_self.decrementMovesCount();
		}
	};
	this.selectDice = function(x){
		_self.diceCollection[x].selectedFlag = !_self.diceCollection[x].selectedFlag;
		brd.drawDice(_self.diceCollection);
	};
	this.decrementMovesCount = function(lock){
		if(_self.movesLeft === 0) {
			_self.rollFlag = false;
			_self.movesLeft = MAX_MOVES_COUNT;	
		}
		else{
			_self.rollFlag = true;
			_self.movesLeft -= 1;
		}
		_self.refresh({lock : lock});
	};
	this.resetMovesLeft = function(){
		_self.movesLeft = MAX_MOVES_COUNT;
	};
};

var yamb = new Yamb({SMALL : false, DICE_COUNT : 5});
$(document).ready(function(){
	$("body").append(yamb.drawBoard());
	$(".board td").click(function(){
		yamb.enterValue($(this).data('hook'));
	});
	$(".roll").click(function(){
		yamb.rollDice();
	});
	$(document).on("click", ".dice", function(){
		yamb.selectDice($(this).data('x'));
	});
});